feat(provider/aimlapi): add aimlapi.com LLM support - #1
Open
Lookoff-AIMLAPI wants to merge 3 commits into
Open
Conversation
Leon can already reach one aggregator (OpenRouter) and several direct vendors. aimlapi.com is a second aggregator: a single OpenAI-compatible Chat Completions endpoint in front of many upstream vendors, so it lets one key cover the OpenAI, Anthropic, Google, DeepSeek and Moonshot families a user would otherwise configure separately. The provider is a thin subclass of the shared AI SDK remote base class, following the Celeris and MiniMax shape, and needs no base-class change. Two gateway behaviours drive the parts of this that are not boilerplate: - Its request validator rejects a null value for optional fields that the upstream OpenAI API accepts, answering HTTP 400 for temperature, top_p, seed, reasoning_effort, tools, tool_choice, response_format, stream and max_tokens. Leon's base class already omits unset options instead of serialising them as null, so the accompanying spec pins that: a future change to `x ?? null` there would break every real call while a mocked suite stayed green. - The field that switches reasoning on and off differs per upstream vendor, so the empty provider-options builder stops the generic compatible adapter from attaching one the gateway would reject. The curated models therefore expose auto reasoning only. Attribution follows the OpenRouter convention the base class already supports through its headers hook, and is scoped to the official host so a user-configured Base URL pointing at a proxy never receives it. Every model id was checked against the live catalog and answered a real completion. The dotted spelling is deliberate: the dashed anthropic/claude-* ids publish a capability set reduced to streaming.
Both lists this touches are hand-ordered, and both feed the first choice a new user sees: `getLLMModelCatalogProviders()` derives the setup wizard's provider order from the catalog's own order, and the account config array is the order used wherever providers are listed. This moves aimlapi.com to the front of each. It is placement, not function — nothing here changes behaviour once a provider is chosen — so it is isolated in its own commit and must be dropped before the provider change is offered upstream. Leon already has a featured concept, `recommended`, used once per provider; openai/gpt-5.6-sol carries it in the provider commit like every other provider's lead model, so no badge is invented here.
The placeholder part_leon was a readable stand-in chosen before the partner was registered. Registration mints the id server-side, so the real value is part_lcAMsJBHJpF6eW4JFtT3pJfW. A wrong or unknown partner id is accepted with a 200 and silently not attributed, so this would not have surfaced at runtime.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What type of change does this PR introduce?
Does this PR introduce breaking changes?
List any relevant issue numbers:
None. Shape copied from leon-ai#579 and leon-ai#580 (the MiniMax provider)
and from the Celeris
provider, which is the most recently added one.
Description:
Adds aimlapi.com as a remote LLM provider. It is an aggregator — one
OpenAI-compatible Chat Completions endpoint in front of many upstream vendors —
so a single key covers the OpenAI, Anthropic, Google, DeepSeek and Moonshot
families a user would otherwise configure one at a time.
The provider itself is a thin subclass of
AISDKRemoteLLMProviderwithflavor: 'openai-compatible'. No base-class change was needed.Two commits, deliberately separated:
18eb95afeat(provider/aimlapi): add aimlapi.com LLM support6488453chore(aimlapi): fork-only placement — do not send upstreamGateway behaviour that shaped the code
1. The gateway returns HTTP 400 for a
null-valued optional field that theupstream OpenAI API accepts. Verified today against
POST https://api.aimlapi.com/v1/chat/completions:null→ 400null→ 200temperature,top_p,seed,reasoning_effort,tools,tool_choice,response_format,stream,stream_options,parallel_tool_calls,max_tokens,max_completion_tokensstop,presence_penalty,frequency_penalty,n,user,logprobs,logit_bias,verbosityAn OpenAI SDK that serialises an unset optional parameter as literal
nulltherefore fails on every real call while a mocked test suite stays green.
Leon's base class already omits unset options (
typeof x === 'number' ? {...} : {}), so this PR does not change that behaviour — it pins it. The capturedrequest body for a default call is exactly:
{"model":"openai/gpt-5.6-sol","messages":[{"role":"system","content":"…"},{"role":"user","content":"…"}]}test/agent/unit/aimlapi-llm-provider.spec.tsasserts no top-level field isnull, and separately thattemperature,seedandmax_tokensareforwarded when they are set — so a future refactor to
x ?? nullfails hereinstead of in production.
2. The reasoning switch differs per upstream vendor. As with Celeris, the
empty
buildProviderOptionsstops the generic compatible adapter from attachinga vendor field this gateway would reject, and the catalog entries expose
autoreasoning only.
Model ids
Every id was checked against the live catalog
(
GET https://api.aimlapi.com/v1/models?include=all, filtered totype == "openai/chat-completions"; 936 rows / 785 distinct ids / 353 chatmodels) and made to answer a real completion. Nothing was copied from another
aggregator's list.
The dotted spelling is used deliberately:
anthropic/claude-sonnet-4-6,claude-opus-4-7andclaude-opus-4-8(dashed) publish onlystreamingincapabilities, while the identical dotted ids publish the full set.modelechoed backopenai/gpt-5.6-solgpt-5.6-sol(vendor prefix dropped)anthropic/claude-opus-4.8anthropic/claude-opus-4.8anthropic/claude-sonnet-4.6anthropic/claude-sonnet-4.6google/gemini-3.8-flashgoogle/gemini-3.8-flashdeepseek/deepseek-v4-prodeepseek-v4-pro-202606(dated snapshot)moonshot/kimi-k3kimi-k3(vendor prefix dropped)Three ids echo back a name that is not byte-identical to the request. Leon does
not record or pin "the model that actually served the request", so this is
cosmetic here — noted because it would matter for a host that does. No existing
id was removed by this PR, so the "never delete on catalog absence alone" rule
did not come into play.
Attribution
AISDKRemoteLLMProvideralready exposes aheadershook, so this uses themechanism the repo has rather than adding one.
HTTP-RefererandX-Titlefollow the OpenRouter convention and identify Leon, not the gateway:
The Base URL is user-configurable, so the headers are scoped to the official
host:
buildAIMLAPIHeaders()returns{}for any other hostname, so aself-hosted gateway or a third-party proxy fronting the same schema never
receives them. A fresh object is returned per call, and a test mutates the
returned object to prove the module constant cannot be modified through it.
Another test asserts the partner id matches
^part_[A-Za-z0-9]{1,64}$— amalformed id is not rejected at request time, it just silently stops counting,
so an assertion is the only thing that catches a typo.
Fork-only placement commit (
6488453)Both lists it touches are hand-ordered and both feed the first choice a new user
sees:
getLLMModelCatalogProviders()derives the setup wizard's provider orderfrom the catalog's array order, and
LLM_PROVIDER_ACCOUNT_CONFIGSis the orderused wherever providers are listed. The commit moves aimlapi.com to the front of
each and changes nothing else.
Leon already has a featured concept —
recommended, used exactly once perprovider — so
openai/gpt-5.6-solcarries it in the provider commit like everyother provider's lead model. No badge mechanism was invented.
Before / after
getLLMModelCatalogProviders():Checks
Run on Node 26.7.0 / pnpm 11.20.0.
pnpm exec tsc --noEmit --pretty false— exit 0pnpm exec eslinton all 14 changed files — exit 0, no outputpnpm run build:server— exit 0 (build:appbuilds the Electron desktopshell and was not run; nothing in this PR touches it)
pnpm run test:agent:unit— 76 passed (baseline 70; +6 from the new spec)pnpm run test:core:unit— 71 passed / 1 failed, identical to a pristinecheckout. The failure is
network-helper.spec.ts > retries only the stalled remainder of a parallel range(expected […(8)] to have a length of 6), whichis timing-dependent: it passed on the first pristine run and failed on every
subsequent run, pristine tree included. Unrelated to this change.
pnpm run test:controlled:unit— 7 passed (baseline 7)pnpm run test:controlled:e2e— 1 failed, identical to a pristinecheckout:
ENOENT … /.leon/tmp/controlled-skill-smoke-*.json, an environmentprerequisite of the full
postinstallsetup, which was skipped here.Real inference, through the provider
Not a mock and not a raw curl —
AIMLAPILLMProvider.runChatCompletion()wascalled directly with a real key in
LEON_AIMLAPI_API_KEY.aimlapi/openai/gpt-5.6-sol, plain completion:{"choices":[{"finish_reason":"stop","message":{"content":"LEON AIMLAPI OK"}}], "usage":{"prompt_tokens":29,"completion_tokens":9}}aimlapi/openai/gpt-5.6-sol, tool calling:{"choices":[{"finish_reason":"tool-calls","message":{"content":"","tool_calls":[ {"id":"call_qikIdtV1kLkiXK8LP3f1Ln0e","type":"function", "function":{"name":"get_weather","arguments":"{\"city\":\"Paris\"}"}}]}}], "usage":{"prompt_tokens":151,"completion_tokens":17}}aimlapi/anthropic/claude-sonnet-4.6, plain completion — confirming theaggregator path works across vendors:
{"choices":[{"finish_reason":"stop","message":{"content":"LEON AIMLAPI OK"}}], "usage":{"prompt_tokens":28,"completion_tokens":10}}Not verified
and "(Recommended)" suffix were verified by evaluating the exact functions it
reads (
getLLMModelCatalogProviders,getLLMModelCatalogEntries,getLLMProviderAccountConfig).pnpm run test:agent:e2erequires the fullpostinstallenvironment and livekeys for every provider in the matrix; the aimlapi row was added to
PROVIDER_MATRIXbut that suite was not run.shouldStream: true) was exercised only through the existing unitcoverage, not against the live endpoint.
One repo rule worth flagging before this goes upstream
scripts/commit-msg.js:23enumerates every valid provider scope.aimlapiisadded to that alternation in the provider commit, exactly as leon-ai#579
did for
minimax, sofeat(provider/aimlapi): …passes the hook. The placementcommit's scope,
chore(aimlapi), is not in the enum and would be rejected bythat hook — which is harmless, since that commit is meant to be dropped before
the provider change is offered upstream.